using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class SystemInterface : Component 
    {
        const string IO_TEST_SOURCE = "io_test_source";
        const string IO_TEST_VIEW = ".io_test";

        public SystemControl SystemControl;
        public System.Collections.Hashtable SignalSources;
        public System.Collections.Hashtable SignalViews;

        private RapidHardware oRapidHardware;

        public SystemInterface(RapidHardware poRapidHardware)
            : base(poRapidHardware,"system_interface")
        {
            SignalSources = new System.Collections.Hashtable();
            SignalViews = new System.Collections.Hashtable();
            oRapidHardware = poRapidHardware;
            SystemControl = new SystemControl(oRapidHardware);
        }

        public SignalSource AddSignalSource(string psName, int piWidth, int piValue)
        {
            SignalSource oSignalSource;
            oSignalSource = GetSignalSource(psName);
            if (oSignalSource != null)
                return oSignalSource;
            oSignalSource = new SignalSource(psName, piWidth, piValue);
            SignalSources.Add(psName, oSignalSource);
            return oSignalSource;
        }

        public SignalSource AddSignalSource(string psName, int piWidth, string psValue)
        {
            SignalSource oSignalSource;
            oSignalSource = GetSignalSource(psName);
            if (oSignalSource != null)
                return oSignalSource;
            oSignalSource = new SignalSource(psName, piWidth, 0);
            oSignalSource.ValueAsString = psValue;
            SignalSources.Add(psName, oSignalSource);
            return oSignalSource;
        }

        public SignalSource AddSignalSource(string psName, NodeVector pnvSink, int piValue)
        {
            SignalSource oSignalSource;
            oSignalSource = AddSignalSource(psName, pnvSink.Nodes.Count, piValue);
            oSignalSource.ConnectNodeVector(pnvSink,piValue);            
            return oSignalSource;
        }

        public SignalSource AddSignalSource(string psName, NodeVector pnvSink, string psValue)
        {
            SignalSource oSignalSource;
            oSignalSource = AddSignalSource(psName, pnvSink.Nodes.Count,psValue);
            oSignalSource.ConnectNodeVector(pnvSink,psValue);
            if (oSignalSource != null)
                oSignalSource.ValueAsString = psValue;
            return oSignalSource;
        }

        public SignalView AddSignalView(string psName, NodeVector pnvNodeVector)
        {
            return AddSignalView(psName, pnvNodeVector, 0, pnvNodeVector.Nodes.Count-1, false);
        }

        public SignalView AddSignalView(string psName,NodeVector pnvNodeVector, int piStartBit, int piEndBit, bool pbPadLeft)
        {
            SignalView oSignalView;
            oSignalView = GetSignalView(psName);
            if (oSignalView != null)
                return oSignalView;

            oSignalView = new SignalView(psName, pnvNodeVector,piStartBit,piEndBit,pbPadLeft);
            SignalViews.Add(psName, oSignalView);
            return oSignalView;
        }

        public SignalView AddSignalView(string psName, string psPath)
        {
            NodeVector oNodeVector;
            oNodeVector = oRapidHardware.Structure.GetNodeVector(psPath);
            return AddSignalView(psName, oNodeVector);
        }

        public SignalView AddSignalView(string psPath)
        {
            NodeVector oNodeVector;
            oNodeVector = oRapidHardware.Structure.GetNodeVector(psPath);
            System.Diagnostics.Debug.Assert(oNodeVector != null,"Bad Path " + psPath);
            return AddSignalView(psPath, oNodeVector);
        }


        public SignalView GetSignalView(string psName)
        {
            if (SignalViews.Contains(psName))
                return (SignalView)SignalViews[psName];
            return null;
        }

        public SignalSource GetSignalSource(string psName)
        {
            if (SignalSources.Contains(psName))
                return (SignalSource)SignalSources[psName];
            return null;
        }

        public bool SetSignal(string psName, string sValue)
        {
            SignalSource oSignalSource = GetSignalSource(psName);
            if (oSignalSource == null)
                return false;
            oSignalSource.ValueAsString = sValue;
            return true;
        }

        public bool SetSignal(string psName, int piValue)
        {
            SignalSource oSignalSource = GetSignalSource(psName);
            if (oSignalSource == null)
                return false;
            oSignalSource.ValueAsInt = piValue;
            return true;
        }

        public string ViewSignal(string psName)
        {
            SignalSource oSignalSource;
            SignalView oSignalView = GetSignalView(psName);

            if (oSignalView != null)
                return oSignalView.ValueAsString;

            oSignalSource = GetSignalSource(psName);
            if (oSignalView != null)
                return oSignalSource.ValueAsString;

            return "x";
        }

        public int ViewSignalAsInt(string psName)
        {
            string sResult = ViewSignal(psName);
            return Conversion.StringBinaryToInt(psName);
        }


        public void ListAll()
        {
            foreach (SignalSource oSignalSource in SignalSources.Values)
            {
                System.Console.WriteLine(oSignalSource.Name + " - " + oSignalSource.ValueAsString);
            }

            foreach (SignalView oSignalView in SignalViews.Values)
            {
                System.Console.WriteLine(oSignalView.Name + " - " + oSignalView.ValueAsString + "\t {" + oSignalView.GetSimulationState() + "}");
            }

        }


        public double BenchmarkIO(int piCountInThousands, int piClockCycles, bool pbWriteOnly)
        {
            SignalSource oSource = RapidHardware.SystemInterface.GetSignalSource(IO_TEST_SOURCE);
            SignalView oView = RapidHardware.SystemInterface.GetSignalView(IO_TEST_VIEW);                      
            Timer oTimer = new Timer();

            int iTransfers = 0;

            RapidHardware.Transcript.AddTranscript("Start IO Benchmark -- \nCount - " + piCountInThousands.ToString() + "K\nClock Cycles - " + piClockCycles.ToString() + "\n WriteOnly - " + pbWriteOnly.ToString());

            oTimer.Start();

            for (int iCount = 0; iCount < piCountInThousands * 4; iCount++)
            {
                for (int iIdx = 0; iIdx < 256; iIdx++)
                {
                    iTransfers++;
                    oSource.ValueAsInt = iIdx;
                    if (piClockCycles > 0)
                        SystemControl.Go(piClockCycles);
                    if (!pbWriteOnly)
                    {
                        if (oView.ValueAsInt != iIdx)
                        {
                            RapidHardware.Transcript.AddTranscript("Benchmark IO Test Failed " +  iIdx.ToString() + "!=" + oView.ValueAsInt.ToString() + " on cycle " + iCount.ToString() );
                            break;
                        }
                    }
                }
            }
            oTimer.Stop();

            double dTPS = iTransfers / oTimer.Duration;
            RapidHardware.Transcript.AddTranscript("IO Transfers - " + iTransfers.ToString() + "\nSeconds - " + oTimer.Duration.ToString() +  "\nTPS - " + dTPS.ToString());
            RapidHardware.Transcript.AddTranscript("IO Test Complete" );
            return dTPS;
        }

        public void HideSignalView(string psName, bool pbHide)
        {
            SignalView oSignalView = GetSignalView(psName);
            if (oSignalView == null)
                return;
            oSignalView.Hidden = pbHide;
        }

        public void HideSignalSource(string psName, bool pbHide)
        {
            SignalSource oSignalSource = GetSignalSource(psName);
            if (oSignalSource == null)
                return;
            oSignalSource.Hidden = pbHide;
        }

        public void FormatSignalView(string psName, SignalFormat psfFormat)
        {
            SignalView oSignalView = GetSignalView(psName);
            if (oSignalView == null)
                return;
            oSignalView.SignalFormat = psfFormat;
        }

        public void FormatSignalSource(string psName, SignalFormat psfFormat)
        {
            SignalSource oSignalSource = GetSignalSource(psName);
            if (oSignalSource == null)
                return;
            oSignalSource.SignalFormat = psfFormat;
        }

        public void ApplySignalSourceDefaults()
        {
            foreach (SignalSource oSignalSource in SignalSources.Values)
            {
                oSignalSource.ValueAsString = oSignalSource.ValueAsString;
            }
        }
    }
}
